package com.hyt.it.ogt.ykcj.service.system.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.hyt.common.utils.exception.BusinessException;
import com.hyt.common.utils.result.Result;
import com.hyt.core.base.BaseServiceImpl;
import com.hyt.core.constant.CommonConstant;
import com.hyt.it.ogt.common.config.GlobalConfig;
import com.hyt.it.ogt.pj.feign.tps.TpsClient;
import com.hyt.it.ogt.ykcj.domain.exam.ExamInfo;
import com.hyt.it.ogt.ykcj.mapper.exam.ExamInfoMapper;
import com.hyt.it.ogt.ykcj.service.system.IFileUpHwCloudService;
import com.hyt.model.tps.vo.Bucket;
import com.obs.services.ObsClient;
import com.obs.services.ObsConfiguration;
import com.obs.services.exception.ObsException;
import com.obs.services.model.CompleteMultipartUploadResult;
import com.obs.services.model.PutObjectResult;
import com.obs.services.model.UploadFileRequest;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 *
 */
@Service
public class FileUpHwCloudServiceImpl extends BaseServiceImpl<ExamInfoMapper, ExamInfo> implements IFileUpHwCloudService {

    private final Logger logger = LoggerFactory.getLogger(FileUpHwCloudServiceImpl.class);

    @Autowired
    private TpsClient tpsClient;

    private static ObsClient obsClient;

    @Resource
    GlobalConfig globalConfig;

    // MB上传转换
    private static final Long MB_SPEED_CONVERT = 1024 * 1024L;

    /**
     * 使用永久凭证，单例模式
     *
     * @return
     */
    public synchronized ObsClient getObsClient() {
        if (obsClient == null) {
            Bucket bucket = getBucketConfig();
            ObsConfiguration config = new ObsConfiguration();
            config.setSocketTimeout(30000);
            config.setConnectionTimeout(10000);
            config.setEndPoint(bucket.getEndPoint());
            // 查找默认配置
            synchronized (this) {
                if (obsClient == null) {
                    obsClient = new ObsClient(bucket.getAccessKey(), bucket.getSecretKey(), config);
                }
            }
        }
        return obsClient;
    }

    @Override
    public Bucket getBucketConfig() {
        Bucket bucket = null;
        try {
            String json = tpsClient.getBucketTempCredential();
            if (StringUtils.isNotEmpty(json)) {
                bucket = JSON.parseObject(json, Bucket.class);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bucket;
    }

    @Override
    public String uploadFileToHuawei(byte[] fileByte, String fileType, String fileName) {
        String fileUrl = null;
        // 获取华为云上传路径
        String hwCloudFileDict = globalConfig.getHwCloudFileDict();
        String path = hwCloudFileDict + fileType + "/" + fileName;
        // 文件上传华为云
        logger.info("# {}文件开始上传华为云", fileType);
        Result result = uploadFileToHuawei("", path, fileByte);
        logger.info("# {}上传华为云：{}", fileType, JSON.toJSONString(result));
        if (result.getSuccess()) {
            fileUrl = result.getObj().toString().replace("%2F", "/");
        }
        return fileUrl;
    }

    @Override
    public String uploadFileToHuawei(File file, String fileType, String fileName) {
        String fileUrl = null;
        // 获取华为云上传路径
        String hwCloudFileDict = globalConfig.getHwCloudFileDict();
        String path = hwCloudFileDict + fileType + "/" + fileName;
        // 文件上传华为云
        logger.info("# {}文件开始上传华为云", fileType);
        Result result = uploadFileToHuawei("", path, file);
        logger.info("# {}上传华为云：{}", fileType, JSON.toJSONString(result));
        if (result.getSuccess()) {
            fileUrl = result.getObj().toString().replace("%2F", "/");
        }
        return fileUrl;
    }

    public Result uploadFileToHuawei(String officeId, String path, byte[] bytes) {
        Result result = new Result();
        try {
            result.setSuccess(true);
            result.setObj(uploadFile(path, bytes));
        } catch (Exception e) {
            logger.error("# 华为云上传文件调用失败：{}", path, e);
            result.setResult(false, "华为云上传文件调用失败");
        }
        return result;
    }

    public Result uploadFileToHuawei(String officeId, String path, File file) {
        Result result = new Result();
        try {
            result.setSuccess(true);
            result.setObj(uploadFile(path, file));
        } catch (Exception e) {
            logger.error("# 18、华为云上传文件调用失败：{}", path, e);
            result.setResult(false, "华为云上传文件调用失败");
        }
        return result;
    }

    /**
     * 断点续传文件上传
     *
     * @param path
     * @param file
     */
    private String uploadFile(String path, File file) {
        Bucket bucket = getBucketConfig();
        UploadFileRequest request = new UploadFileRequest(bucket.getBucket(), path);
        // 设置待上传的本地文件，localfile为待上传的本地文件路径，需要指定到具体的文件名
        request.setUploadFile(file.getPath());
        // 设置分段上传时的最大并发数
        request.setTaskNum(globalConfig.getTaskNum());
        // 设置分段大小为100MB
        request.setPartSize(globalConfig.getPartSize() * MB_SPEED_CONVERT);
        // 开启断点续传模式
        request.setEnableCheckpoint(true);
        // 设置数据传输监听器，用于获取上传进度
        request.setProgressListener(status -> {
            try {
                // 获取上传平均速率
                logger.info("{}文件上传平均速率:{}MB/S", path, BigDecimal.valueOf(status.getAverageSpeed()).divide(BigDecimal.valueOf(MB_SPEED_CONVERT)).setScale(CommonConstant.TWO, RoundingMode.HALF_DOWN));
                // 获取上传进度百分比
                logger.info("{}文件上传进度百分比:{}%", path, status.getTransferPercentage());
            } catch (Exception e) {

            }
        });
        // 数据反馈上传进度
        request.setProgressInterval(globalConfig.getProgressInterval() * MB_SPEED_CONVERT);
        logger.info("# 14、{}断点续传请求信息：{}", path, request);
        // 进行断点续传上传
        return uploadFile(request);
    }

    /**
     * 文件断点续传到华为云 在这里会进行失败重试
     *
     * @param request
     * @return
     */
    private String uploadFile(UploadFileRequest request) {
        // 是否异常
        boolean hasError;
        // 文件上传异常次数
        Integer exceptionNum = 0;
        // 文件上传成功url
        String fileUrl = null;
        // 上传开始时间
        DateTime startDateTime = DateUtil.date();

        ObsClient obsClient = getObsClient();
        do {
            hasError = Boolean.FALSE;
            try {
                // 进行断点续传上传
                CompleteMultipartUploadResult result = obsClient.uploadFile(request);
                logger.info("# 15、断点续传响应信息：{}", result);
                fileUrl = result.getObjectUrl();
            } catch (ObsException e) {
                // 发生异常时可再次调用断点续传上传接口进行重新上传
                hasError = Boolean.TRUE;
                exceptionNum++;
                // 是否大于重试次数
                if (exceptionNum > globalConfig.getRetryNum()) {
                    throw new BusinessException("文件{}上传异常，失败重试次数已超最大值", request.getObjectKey());
                }
                logger.error("# 16、{}文件上传异常，失败重试第{}次", request.getObjectKey(), exceptionNum);
            }
        } while (hasError);
        // 上传结束时间
        DateTime endDateTime = DateUtil.date();
        logger.info("# 17、文件上传统计：{}文件上传开始时间{}，上传结束时间{}，上传失败{}次，总耗时{}", request.getObjectKey(), startDateTime, endDateTime, exceptionNum, DateUtil.formatBetween(startDateTime, endDateTime));
        return fileUrl;
    }

    public Object uploadFile(String path, byte[] bytes) {
        Bucket bucket = getBucketConfig();
        PutObjectResult putObjectResult = getObsClient().putObject(bucket.getBucket(), path, new ByteArrayInputStream(bytes), null);
        int statusCode = putObjectResult.getStatusCode();
        if (statusCode != HttpServletResponse.SC_OK) {
            throw new RuntimeException("华为云OBS文件上传异常");
        }
        logger.info("# 华为云存储上传文件返回路径：{}", putObjectResult.getObjectUrl());
        return putObjectResult.getObjectUrl();
    }
}
